﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Myotragus.Data.Util;

namespace Myotragus.Data.Metadata
{
	public class MetadataEntityMapping : IEntityMapping
	{
		public virtual IPrimaryKeyPropertyMapping GetPrimaryKey(Type interfaceType)
		{
			var idProperty = ReflectionHelper.GetProperties(interfaceType).FirstOrDefault(IsId);

			return idProperty == null ? null
				: new PrimaryKeyPropertyMapping
				{
					Property = idProperty,
					ColumnName = GetIdColumn(interfaceType),
					Generator = GetIdGenerator(interfaceType)
				};
		}
		protected IEnumerable<IPropertyMapping> FindPropertiesMathing(Type interfaceType, Func<PropertyInfo, bool> predicate)
		{
			return ReflectionHelper.GetProperties(interfaceType)
				.Where(predicate).Select(p => (IPropertyMapping)
			  new PropertyMapping
			  {
				  ColumnName = p.Name,
				  Property = p
			  });
		}
		public virtual string GetEntityTable(Type interfaceType)
		{
			return GetEntityTableName(interfaceType);
		}
		public virtual IEnumerable<IPropertyMapping> GetFields(Type interfaceType)
		{
			return FindPropertiesMathing(interfaceType, p => !IsId(p));
		}

		private static readonly Type TypeOfEntityAttribute = typeof(IEntityAttribute);
		private static readonly Type TypeOfIdAttribute = typeof(IdAttribute);

		private static string GetEntityTableName(Type @this)
		{
			var attribute = @this.GetCustomAttributes(TypeOfEntityAttribute, true)
				.Cast<IEntityAttribute>().FirstOrDefault();

			return attribute == null || String.IsNullOrEmpty(attribute.Table)
					? @this.Name : attribute.Table;
		}

		private static string GetIdGenerator(Type @this)
		{
			var attId = GetEntityAttribute(@this) ?? new EntityAttribute();

			return attId.IdGenerator.ToLower();
		}

		private static IEntityAttribute GetEntityAttribute(Type @this)
		{
			return @this.GetCustomAttributes(TypeOfEntityAttribute, true).Cast<IEntityAttribute>()
				.FirstOrDefault();
		}

		private static string GetIdColumn(Type @this)
		{
			var attId = GetEntityAttribute(@this);

			if (attId == null || String.IsNullOrEmpty(attId.IdColumn))
				return GetIdProperty(@this).Name;

			return attId.IdColumn;
		}

		private static bool IsId(PropertyInfo @this)
		{
			return @this.GetCustomAttributes(TypeOfIdAttribute, true).Any();
		}

		private static PropertyInfo GetIdProperty(Type @this)
		{
			return ReflectionHelper.GetProperties(@this).Where(IsId).FirstOrDefault();
		}
	}
}